/*
Copyright (c) 2011, Stephane Albert
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the organization nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL STEPHANE ALBERT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "debridFetcherAD.h"

debridFetcherAD::debridFetcherAD() {
    cookieShare = NULL;
    torrentProcessor = NULL;
    createShare();
    pthread_mutex_init(&cookieLock, NULL);
}

debridFetcherAD::~debridFetcherAD() {
    delete torrentProcessor;
    //We must stop every transfer before we logout (curl struct corruption)
    stop();
    // Logout from service
    logout();
    pthread_mutex_destroy(&cookieLock);
    curl_share_cleanup(cookieShare);
}

void debridFetcherAD::createShare() {
    cookieShare = curl_share_init();
    if (!cookieShare)
        return;
    curl_share_setopt(cookieShare, CURLSHOPT_LOCKFUNC, lockCookie);
    curl_share_setopt(cookieShare, CURLSHOPT_UNLOCKFUNC, unlockCookie);
    curl_share_setopt(cookieShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
    curl_share_setopt(cookieShare, CURLSHOPT_USERDATA, (void *)&cookieLock);

    curl_easy_setopt(curlHandle, CURLOPT_SHARE, cookieShare);
}

void debridFetcherAD::lockCookie(CURL *handle, curl_lock_data data, curl_lock_access access, void *userptr) {
    pthread_mutex_lock((pthread_mutex_t *)userptr);
}

void debridFetcherAD::unlockCookie(CURL *handle, curl_lock_data data, void *userptr) {
    pthread_mutex_unlock((pthread_mutex_t *)userptr);
}

bool debridFetcherAD::authClient(const std::string& username, const std::string& password) {
    if (curlHandle != NULL) {
        std::string URL = "http://www.alldebrid.com/register/?action=login&login_login="+username+"&login_password="+password;
        std::string retPage = getData(URL, false);
        long responseCode;
        if (curl_easy_getinfo(curlHandle, CURLINFO_RESPONSE_CODE, &responseCode) == CURLE_OK) {
            if (responseCode == 302) {   //Redirect
                return true;
            }
            else {      //Check if we're considered as robots, because we aren't ;)
                size_t loginFormPos = retPage.find("<form method=\"get\" action=\"/register/\" name=\"connectform\">");
                if (loginFormPos != std::string::npos) {
                    size_t loginFormEndPos = retPage.find("</form>", loginFormPos);
                    if (loginFormEndPos != std::string::npos) {
                        if (retPage.find("<script type=\"text/javascript\" src=\"http://www.google.com/recaptcha/api/challenge?", loginFormPos) < loginFormEndPos) {   //Guess what? They think we are a bot.
                            std::cerr << _("AllDebrid is asking for a captcha, please log in to the website again and retry.") << std::endl;
                        }
                    }
                }
            }
        }
    //We can then check for a redirect URL to www.alldebrid.com but it's still unreliable
    }
    return false;
}

std::string debridFetcherAD::debridLink(const std::string &linkURL) {
    if (curlHandle == NULL)
        return _("CURL not init.");
    logMessage(linkURL, DEBRID);
    //Post AJAX Like
    std::string ajaxLinkURL = "http://www.alldebrid.com/service.php?json=true&link=";
    ajaxLinkURL.append(linkURL);
    std::string retPage = getData(ajaxLinkURL);
    if (retPage.length() == 0)
        return _("Unable to fetch AJAX request.");
    //JSON Error Handling
    size_t begPos = retPage.rfind("error");
    if (begPos != std::string::npos) {
        begPos += 6;
        begPos = retPage.find('"', begPos);     //Easier to find the next string than searching for ":" as there can be spaces
        ++begPos;
        size_t endPos = retPage.find('"', begPos);
        if (endPos != begPos) {                 //Error
            std::string errorMsg = retPage.substr(begPos, endPos-begPos);
            if (!errorMsg.compare("premium"))
                errorMsg = _("premium expired.");
            return errorMsg;
        }
    }
    begPos = retPage.find("link");
    if (begPos != std::string::npos) {
        begPos += 5;
        begPos = retPage.find('"', begPos);     //Easier to find the next string than searching for ":" as there can be spaces
        ++begPos;
        size_t endPos = retPage.find('"', begPos);
        std::string debridedLink = retPage.substr(begPos, endPos-begPos);
        begPos = debridedLink.find('\\');       //Remove escaping caracters
        while (begPos != std::string::npos) {
            debridedLink.erase(begPos, 1);
            begPos = debridedLink.find('\\', begPos);
        }
        if (debridedLink.compare(linkURL))      //On error same link is returned
            return debridedLink;
    }
    return _("Unknown Error");
}

void debridFetcherAD::logout() {
    if (curlHandle == NULL)
        return;
    //In the new version we need to get the mainpage to get the logout key
    std::string mainPage = getData("http://www.alldebrid.com");
    if (mainPage.length() == 0)
        return;
    size_t linkPos = mainPage.rfind("<a class=\"toolbar_disconnect\" href=\"");
    if (linkPos != std::string::npos) {
        linkPos += 36;
        size_t linkEndPos = mainPage.find('"', linkPos);
        std::string logoutLink = "http://www.alldebrid.com";
        logoutLink.append(mainPage.substr(linkPos, linkEndPos-linkPos));
        getData(logoutLink);
    }
}

bool debridFetcherAD::isDebrid(const std::string& link) {
    if (link.find("alldebrid.") != std::string::npos)
        return true;
    return false;
}

bool debridFetcherAD::processServiceSpecific(const std::string& path) {
    //size_t pathLen = path.length();
    /*if (!path.compare(0, 7, "magnet:")) {
        if (torrentProcessor->addMagnet(path))
            return true;
    }
    else if (pathLen > 8 && !path.compare(pathLen-8, 8, ".torrent")) {
        if (torrentProcessor->addTorrent(path))
            return true;
    }*/
    if (torrentProcessor->addMagnet(path))
        return true;
    if (torrentProcessor->addTorrent(path))
        return true;
    return false;
}

void debridFetcherAD::startServiceSpecific() {
    if (torrentProcessor == NULL && cookieShare) {
        torrentProcessor = new debridADTorrent(curlHandle, cookieShare, this);
    }
}

/*void debridFetcherAD::stopServiceSpecific() {
    if (torrentProcessor != NULL) {
        delete torrentProcessor;
        torrentProcessor = NULL;
    }
}*/

bool debridFetcherAD::checkFileForError(const std::string& filePath) {
    std::ifstream inFile;
    inFile.open(filePath.c_str(), std::ios_base::binary);
    if (!inFile.good())
        return true;
    inFile.seekg(0, std::ios_base::end);
    if (inFile.tellg() < 5120) {		//If size < 5Kb might be an error message
        char buffer[128];
        inFile.seekg(168, std::ios_base::beg);
        inFile.read(buffer, 127);
        buffer[inFile.gcount()] = '\0';
        std::string errorMsg = "An error occured while processing your request";
        if (errorMsg.compare(0, 46, buffer, 46) == 0) {
            return true;
        }
    }
    return false;
}
